//----- Include Files ---------------------------------------------------------
#include <avr/io.h>	// include I/O definitions (port names, pin names, etc)
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <avr/eeprom.h>

#include "robbus_master.h"

// button layout
// G R
// 4 5
// 3 2
// 0 1

#define BUTTON_PORT PORTC
#define BUTTON_PINS PINC
#define BUTTON_COUNT 6
#define BUTTON_MASK  ((1<<PC0)|(1<<PC1)|(1<<PC2)|(1<<PC3)|(1<<PC4)|(1<<PC5))

#define BF_PRESSED (1<<0)
#define BF_COMPLETED (1<<1)
#define BF_SHORT_PRESS (1<<2)
#define BF_LONG_PRESS (1<<3)
#define BF_ULTRALONG_PRESS (1<<4)
#define BF_DEBOUNCE_RELEASE (1<<5)

#define TIME_SHORT_PRESS 2
#define TIME_LONG_PRESS 100
#define TIME_ULTRALONG_PRESS 200

#define LED_PORT PORTD
#define LED_DDR DDRD
#define RED_LED (1<<PD3)
#define GREEN_LED (1<<PD4)

// bit 0 - on/off, bit 1 - hi/lo
#define LED_ON 1
#define LED_OFF 0
#define LED_HIGH 2
#define LED_LOW 0

#define TAIL_LEFT_INDICATOR_MASK (1 << 0)
#define TAIL_RIGHT_INDICATOR_MASK (1 << 1)

// EEPROM data
#define EE_INITIALIZED_FLAG 'I'
#define EE_MAIN_MODES_COUNT 4
#define EE_HEAD_MODES_COUNT 4
#define EE_TAIL_MODES_COUNT 4
#define EE_HEAD_BLOCK_SIZE 3
#define EE_TAIL_BLOCK_SIZE 2
static uint8_t EEMEM eeBlank = 0; // keep first byte empty (may get trashed
static uint8_t EEMEM eeRobbusFlag = 'R';
static uint8_t EEMEM eeRobbusAddress = 'o';
static uint8_t EEMEM eeInitializedFlag = EE_INITIALIZED_FLAG;
static uint8_t EEMEM eeMainModeCount = EE_MAIN_MODES_COUNT;
static uint8_t EEMEM eeHeadModeCount = EE_HEAD_MODES_COUNT;
static uint8_t EEMEM eeTailModeCount = EE_TAIL_MODES_COUNT;
static uint8_t EEMEM eeMainModes[EE_MAIN_MODES_COUNT] = {0x00, 0x11, 0x22, 0x33};
static uint8_t EEMEM eeHeadModes[EE_HEAD_MODES_COUNT][EE_HEAD_BLOCK_SIZE] = {{0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}, {0x00, 0x00, 0x00}};
static uint8_t EEMEM eeTailModes[EE_MAIN_MODES_COUNT][EE_TAIL_BLOCK_SIZE] = {{0x00, 0x01}, {0x00, 0x0a}, {0x00, 0x50}, {0x00, 0xff}};

// TODO temporary till master will be ready
static uint8_t outData[ROBBUS_OUTGOING_SIZE];

// node data
static uint8_t tailFlags = 0;
static uint8_t tailPwm = 0;

static uint8_t currentMainMode = 0;
static uint8_t currentHeadMode = 0;
static uint8_t currentTailMode = 0;

// button data structuresi
static uint8_t buttonFlags[BUTTON_COUNT];
static uint8_t buttonStamps[BUTTON_COUNT];

static volatile uint8_t stampValue;

// Timer0 overflow interrupt
ISR(TIMER0_OVF_vect) {
	stampValue++; // increment stamp value
}

// TODO temporary till master will be ready
static uint8_t* messageHandler(uint8_t *inData) {
	return outData;
}

static void LoadHeadMode(void) {
	if (eeprom_read_byte(&eeInitializedFlag) == EE_INITIALIZED_FLAG) {
		// read current state from EEPROM
		uint8_t temp  = eeprom_read_byte(&eeHeadModes[currentHeadMode][0]);
		temp = eeprom_read_byte(&eeHeadModes[currentHeadMode][1]);
		temp = eeprom_read_byte(&eeHeadModes[currentHeadMode][2]);
	} else { // use default value
		//outData[0] = 0;
		//outData[1] = 0;
		//outData[2] = 0;
	}
}

static void LoadTailMode(void) {
	if (eeprom_read_byte(&eeInitializedFlag) == EE_INITIALIZED_FLAG) {
		// read current state from EEPROM
		tailFlags = eeprom_read_byte(&eeTailModes[currentTailMode][0]);
		tailPwm = eeprom_read_byte(&eeTailModes[currentTailMode][1]);
	} else { // use default value
		tailFlags = 0;
		tailPwm = 10;
	}
}

static void LoadNextMainMode(void) {
	if (eeprom_read_byte(&eeInitializedFlag) == EE_INITIALIZED_FLAG) {
		currentMainMode = (currentMainMode + 1) % EE_MAIN_MODES_COUNT;
		// read current state from EEPROM
		uint8_t modes = eeprom_read_byte(&eeMainModes[currentMainMode]);
		currentHeadMode = (modes >> 4) & 0x0f;
		currentTailMode = modes & 0x0f;
	} else { // use default value
	}
	
	LoadHeadMode();
	LoadTailMode();
}

static void SyncHead(void) {
	//outData[0] = 0;
	//outData[1] = 0;
	//outData[2] = 0;
	//RobbusMaster_Send('h', 3, outData);
}

static void SyncTail(void) {
	outData[0] = tailFlags;
	outData[1] = tailPwm;
	RobbusMaster_Send('t', 2, outData);
}

static void StatusLed(uint8_t led, uint8_t ledFlags) {
	if (ledFlags & LED_ON) {
		if (ledFlags & LED_HIGH) {
			LED_DDR |= led;
		} else {
			LED_DDR &= ~led;
		}
		LED_PORT |= led;
	} else {
		LED_DDR  &= ~led;
		LED_PORT &= ~led;
	}
}

static void ShortPressAction(uint8_t buttonIndex) {
	if (buttonIndex == 2) {
		tailFlags &= ~TAIL_LEFT_INDICATOR_MASK;
		tailFlags ^= TAIL_RIGHT_INDICATOR_MASK;
	} else 	if (buttonIndex == 3) {
		tailFlags ^= TAIL_LEFT_INDICATOR_MASK;
		tailFlags &= ~TAIL_RIGHT_INDICATOR_MASK;
	}
	SyncHead(); // TODO make async
	SyncTail();
}

static void LongPressAction(uint8_t buttonIndex) {
	if (buttonIndex == 4) {
		LoadNextMainMode();
	}
	SyncHead(); // TODO make async
	SyncTail();
}

static void UltralongPressAction(uint8_t buttonIndex) {
}

static uint8_t GetTimeDifference(uint8_t index) {
	uint8_t startValue = buttonStamps[index];
	uint8_t currentValue = stampValue;

	if (currentValue >= startValue) {
		return currentValue - startValue;
	} else {
		return startValue + currentValue;
	}
}

static void init(void)
{
	// initialize library units
	RobbusMaster_Init(messageHandler);

	// activate pull-ups for buttons
	PORTC |= BUTTON_MASK;

	// configure counter 0 to timestamp buttons
	TCCR0 |= (1 << CS01) | (1 << CS00); // 1:64 prescaler (interrupt every 1/122s)
	TIMSK |= (1 << TOIE0);
	
	// this is to touch the eeprom variables (and have addresses correct	
	volatile uint8_t ee = eeprom_read_byte(&eeBlank);
	ee += eeprom_read_byte(&eeRobbusFlag);
	ee += eeprom_read_byte(&eeRobbusAddress);
	ee += eeprom_read_byte(&eeInitializedFlag);
	ee += eeprom_read_byte(&eeMainModeCount);
	ee += eeprom_read_byte(&eeTailModeCount);
	ee += eeprom_read_byte(&eeHeadModeCount);

	sei();
}

//----- Begin Code ------------------------------------------------------------


int main(void)
{
	init();
	
	while(1) {
		uint8_t buttonsValue =  BUTTON_PINS & BUTTON_MASK; // read buttons

		if(buttonsValue == BUTTON_MASK) { // no button pressed, keep timer on 0 (to have first interval in full length)
			TCNT0 = 0;
			stampValue = 0;
		}

		// go over all buttons
		uint8_t i;
		uint8_t tempFlags = 0;
		for (i = 0; i < BUTTON_COUNT; i++) {
			if (BUTTON_PINS & (1 << i)) { // button not pressed
				if (buttonFlags[i] & BF_PRESSED) {
					if (buttonFlags[i] & BF_ULTRALONG_PRESS) {
						UltralongPressAction(i);
					} else if (buttonFlags[i] & BF_LONG_PRESS) {
						LongPressAction(i);
					} else if (buttonFlags[i] * BF_SHORT_PRESS) {
						ShortPressAction(i);
					}
				}
				buttonFlags[i] = 0;
			} else { // button pressed
				if (!buttonFlags[i] & BF_PRESSED) { // first case whe pressed detected
					buttonFlags[i] |= BF_PRESSED;
					buttonStamps[i] = stampValue;
				} else { // pressed for some time, check intervals
					if (GetTimeDifference(i) > TIME_ULTRALONG_PRESS) {
						buttonFlags[i] |= BF_ULTRALONG_PRESS;
					} else 	if (GetTimeDifference(i) > TIME_LONG_PRESS) {
						buttonFlags[i] |= BF_LONG_PRESS;
					} else 	if (GetTimeDifference(i) > TIME_SHORT_PRESS) {
						buttonFlags[i] |= BF_SHORT_PRESS;
					}
				}
			}

			tempFlags |= buttonFlags[i];
		}

		// indicator - short - low green, long high green, ultralong - high red and green
		if (tempFlags & BF_SHORT_PRESS) {
			StatusLed(GREEN_LED, LED_ON | LED_LOW);
			 
			if  (tempFlags & BF_LONG_PRESS) {
				StatusLed(GREEN_LED, LED_ON | LED_HIGH);
				if (tempFlags & BF_ULTRALONG_PRESS) {
					StatusLed(RED_LED, LED_ON | LED_HIGH);
				}
			}
		} else {
			StatusLed(GREEN_LED, LED_OFF);
			StatusLed(RED_LED, LED_OFF);
		}

		// and reset watchdog
		wdt_reset();
	} // end of main loop
	return 0;
}
